classDiagram
    direction TB

    namespace PublicClasses {
        class DeviceHandle {
            +string media
            +string name
            +string addr
            +string serial

            +Serialize() string
            +ToString() string
        }

        class DeviceRegistryEntry {
            <<abstract>>
            +enumerate(DeviceHandle hint) DeviceHandle[] *
            +make(DeviceHandle handle) SDRDevice *
        }

        class DeviceRegistry {
            +enumerate(DeviceHandle hint) DeviceHandle[] $
            +makeDevice(DeviceHandle handle) SDRDevice$
            +freeDevice(SDRDevice conn)$
            +moduleNames() string[]$
        }

        class LMS7002M {
            -ISPI controlPort
            +Device control functions()
        }

        class SDRDevice {
            <<abstract>>
            +Configure(...) *
            +GetDescriptor() SDRDescriptor *

            +Init() *
            +Reset() *

            +GetGPSLock(...) *
            +EnableChannel(...) *

            +GetClockFreq(...) double*
            +SetClockFreq(...) *

            +GetFrequency(...) double*
            +SetFrequency(...) *

            +GetNCOFrequency(...) double*
            +SetNCOFrequency(...) *

            +GetNCOOffset(...) double*

            +GetNCOIndex(...) int*
            +SetNCOIndex(...) *

            +GetSampleRate(...) double*
            +SetSampleRate(...) *

            +GetGain(...) *
            +SetGain(...) *

            +GetLowPassFilter(...) double*
            +SetLowPassFilter(...) *

            +GetAntenna(...) uint8_t*
            +SetAntenna(...) *

            +GetTestSignal(...) TestSignal*
            +SetTestSignal(...) *

            +GetDCOffsetMode(...) bool*
            +SetDCOffsetMode(...) *

            +GetDCOffset(...) complex64f_t*
            +SetDCOffset(...) *

            +GetIQBalance(...) complex64f_t*
            +SetIQBalance(...) *

            +GetCGENLocked(...) bool*
            +GetTemperature(...) double*
            +GetSXLocked(...) bool*

            +ReadRegister(...) uint*
            +WriteRegister(...) *

            +LoadConfig(...) *
            +SaveConfig(...) *
            
            +GetParameter(...) uint16_t*
            +SetParameter(...) *

            +Calibrate(...) *

            +ConfigureGFIR(...) *
            +GetGFIRCoefficients(...) double[]*
            +SetGFIRCoefficients(...) *
            +SetGFIR(...) *

            +Synchronize(...) *
            +EnableCache(...) *

            +GetHardwareTimestamp(...) uint64_t*
            +SetHardwareTimestamp(...) *

            +StreamSetup(...) *
            +StreamStart(...) *
            +StreamStop(...) *

            +StreamRx(...) uint32_t*
            +StreamTx(...) uint32_t*
            +StreamStatus(...) *

            +UploadTxWaveform(...)

            +SPI(...)
            +I2CWrite(...)
            +I2CRead(...)

            +GPIOWrite(...)
            +GPIORead(...)
            +GPIODirWrite(...)
            +GPIODirRead(...)

            +CustomParameterWrite(...)
            +CustomParameterRead(...)

            +SetMessageLogCallback(...)

            +GetInternalChip(...) *

            +UploadMemory(...)

            +MemoryWrite(...)
            +MemoryRead(...)

            +OEMTest(...)
            +WriteSerialNumber(...)
        }
    }

    %%namespace DeviceEntries {
        class DeviceFactoryFX3 {
            +enumerate(DeviceHandle hint) DeviceHandle[]
            +make(DeviceHandle handle) SDRDevice
        }

        class DeviceFactoryFTDI {
            +enumerate(DeviceHandle hint) DeviceHandle[]
            +make(DeviceHandle handle) SDRDevice
        }

        class DeviceFactoryPCIe {
            +enumerate(DeviceHandle hint) DeviceHandle[]
            +make(DeviceHandle handle) SDRDevice
        }

        class USBEntry {
            <<abstract>>
            +enumerate(DeviceHandle hint) DeviceHandle[]
            +make(DeviceHandle handle) SDRDevice *
        }
    %%}

    %%namespace USBCommunications {
        class LMS64C_LMS7002M_Over_USB {
            +SPI(...)
            +ResetDevice(...) int
        }

        class LMS64C_FPGA_Over_USB {
            +SPI(...)

            +GPIODirRead(...) int *
            +GPIORead(...) int *
            +GPIODirWrite(...) int *
            +GPIOWrite(...) int *

            +CustomParameterWrite(...) int *
            +CustomParameterRead(...) int *

            +ProgramWrite(...) int *

            +MemoryWrite(...) *
            +MemoryRead(...) *
        }

        class USB_CSR_Pipe_Mini {
            +Write(...) int
            +Read(...) int
            #FT601 port
        }

        class USB_CSR_Pipe_SDR {
            +Write(...) int
            +Read(...) int
            #FX3 port
        }

        class IUSB {
            <<interface>>
            +enumerateDevices()*
            +Connect(...) bool*
            +IsConnected() bool*
            +Disconnect()*

            +BulkTransfer(...) int32_t*
            +ControlTransfer(...) int32_t*

            +AllocateAsyncContext()*

            +BeginDataXfer(...) int*
            +WaitForXfer(...) bool*
            +FinishDataXfer(...) int*
            +AbortXfer(...)*
            +FreeAsyncContext()*
        }

        class FT601 {
            +enumerateDevices()
            +Connect(...) bool
            +IsConnected() bool
            +Disconnect()

            +BulkTransfer(...) int32_t
            +ControlTransfer(...) int32_t

            +AllocateAsyncContext()

            +BeginDataXfer(...) int
            +WaitForXfer(...) bool
            +FinishDataXfer(...) int
            +AbortXfer(...)
            +FreeAsyncContext()
        }

        class FX3 {
            +enumerateDevices()
            +Connect(...) bool
            +IsConnected() bool
            +Disconnect()

            +BulkTransfer(...) int32_t
            +ControlTransfer(...) int32_t

            +AllocateAsyncContext()

            +BeginDataXfer(...) int
            +WaitForXfer(...) bool
            +FinishDataXfer(...) int
            +AbortXfer(...)
            +FreeAsyncContext()
        }

        class USB_CSR_Pipe {
            <<abstract>>
            +Write(...) int *
            +Read(...) int *
        }

        class USBGeneric {
            +enumerateDevices()
            +Connect(...) bool
            +IsConnected() bool
            +Disconnect()

            +BulkTransfer(...) int32_t
            +ControlTransfer(...) int32_t

            +AllocateAsyncContext()

            +BeginDataXfer(...) int
            +WaitForXfer(...) bool
            +FinishDataXfer(...) int
            +AbortXfer(...)
            +FreeAsyncContext()
        }

        class USBDMAEmulation {
            +Enable(bool)
            +EnableContinuous(...)
            +GetCounters() State
            +SubmitRequest(...)
            +Wait()
            +BufferOwnership(...)
            +GetBuffers() Buffer[]

            -AbortAllTransfers()
            -UpdateProducerStates()
        }
    %%}

    %%namespace Devices {
        class LimeSDR {
            +Configure(...)

            +Init(...)
            +Reset(...)

            +EnableChannel(...)

            +GetClockFreq(...)double
            +SetClockFreq(...)

            +SetSampleRate(...)

            +SPI(...)

            +StreamSetup(...)

            +StreamStart(...)
            +StreamStop(...)

            +GetInternalChip(...)*

            +GPIODirRead(...)
            +GPIORead(...)
            +GPIODirWrite(...)
            +GPIOWrite(...)

            +CustomParameterWrite(...)
            +CustomParameterRead(...)

            +UploadMemory(...)

            +MemoryRead(...)
            +MemoryWrite(...)

            #GetDeviceInfo() SDRDescriptor
            #ResetUSBFIFO()
            #UpdateFPGAInterface(...) $

            -USBGeneric mStreamPort
            -ISerialPort mSerialPort
            -IComms mlms7002mPort
            -IComms mfpgaPort
        }

        class LimeSDR_Mini {
            +Configure(...)

            +Init(...)
            +Reset(...)

            +SetSampleRate(...)

            +GetClockFreq(...)double
            +SetClockFreq(...)

            +GetTemperature(...)double

            +Synchronize(...)

            +SPI(...)

            +StreamSetup(...)
            +StreamStart(...)
            +StreamStop(...)

            +GPIODirRead(...)
            +GPIORead(...)
            +GPIODirWrite(...)
            +GPIOWrite(...)

            +CustomParameterWrite(...)
            +CustomParameterRead(...)

            +SetSerialNumber(...)

            #GetDeviceInfo() SDRDescriptor
            #UpdateFPGAInterface(...) $

            -USBGeneric mStreamPort
            -ISerialPort mSerialPort
            -IComms mlms7002mPort
            -IComms mfpgaPort
        }

        class LimeSDR_X3 {
            +Configure(...)

            +Init(...)
            +Reset(...)

            +GetSampleRate(...)double
            +SetSampleRate(...)

            +GetClockFreq(...)double
            +SetClockFreq(...)

            +SPI(...)

            +StreamSetup(...)
            +StreamStop(...)

            +CustomParameterWrite(...)
            +CustomParameterRead(...)

            +UploadMemory(...)

            +MemoryRead(...)
            +MemoryWrite(...)

            +UploadTxWaveform(...)

            -CDCM_Dev mClockGeneratorCDCM
            -Equalizer mEqualizer
            -LimePCIe mTRXStreamPorts[]
            -SlaveSelectShim mLMS7002Mcomms[3]
            -IComms fpgaPort
        }

        class LimeSDR_XTRX {
            +Configure(...)

            +Init(...)

            +SetSampleRate(...)

            +GetClockFreq(...)double
            +SetClockFreq(...)

            +SPI(...)

            +StreamSetup(...)
            +StreamStop(...)

            +CustomParameterWrite(...)
            +CustomParameterRead(...)

            +UploadMemory(...)

            +MemoryWrite(...)
            +MemoryRead(...)

            +OEMTest(...)
            +WriteSerialNumber(...)

            -IComms lms7002mPort
            -IComms fpgaPort
            -LimePCIe mStreamPort
            -ISerialPort mSerialPort
        }

        class LimeSDR_MMX8 {
            +Configure(...)
            +GetDescriptor() SDRDescriptor

            +Init()
            +Reset()

            +GetGPSLock(...)
            +EnableChannel(...)

            +GetFrequency(...) double
            +SetFrequency(...)

            +GetNCOFrequency(...) double
            +SetNCOFrequency(...)

            +GetNCOIndex(...) int
            +SetNCOIndex(...)

            +GetNCOOffset(...) double

            +GetSampleRate(...) double
            +SetSampleRate(...)

            +GetLowPassFilter(...) double
            +SetLowPassFilter(...)

            +GetAntenna(...) uint8_t
            +SetAntenna(...)

            +GetTestSignal(...) TestSignal
            +SetTestSignal(...)

            +GetClockFreq(...) double
            +SetClockFreq(...)

            +GetGain(...)
            +SetGain(...)

            +GetDCOffsetMode(...) bool
            +SetDCOffsetMode(...)

            +GetDCOffset(...) complex64f_t
            +SetDCOffset(...)

            +GetIQBalance(...) complex64f_t
            +SetIQBalance(...)

            +GetCGENLocked(...) bool
            +GetTemperature(...) double
            +GetSXLocked(...) bool

            +ReadRegister(...) uint
            +WriteRegister(...)

            +LoadConfig(...)
            +SaveConfig(...)
            
            +GetParameter(...) uint16_t
            +SetParameter(...)

            +Synchronize(...)
            +EnableCache(...)

            +Calibrate(...)

            +ConfigureGFIR(...)
            +GetGFIRCoefficients(...) double[]
            +SetGFIRCoefficients(...)
            +SetGFIR(...)

            +GetHardwareTimestamp(...) uint64_t
            +SetHardwareTimestamp(...)

            +StreamSetup(...)
            +StreamStart(...)
            +StreamStop(...)

            +StreamRx(...) uint32_t
            +StreamTx(...) uint32_t
            +StreamStatus(...)

            +SPI(...)

            +CustomParameterWrite(...)
            +CustomParameterRead(...)

            +SetMessageLogCallback(...)

            +GetInternalChip(...)

            +UploadMemory(...)

            +MemoryWrite(...)
            +MemoryRead(...)

            +UploadTxWaveform(...)
            
            -IComms mMainFPGAcomms
            -LimePCIe mTRXStreamPorts[]
            -LimeSDR_XTRX mSubDevices[]
            -ADF4002 mADF
        }
    %%}

    %%namespace Interfaces {
        class ISPI {
            <<interface>>
            +SPI(...) *
        }

        class IComms {
            <<interface>>
            +GPIODirRead(...) *
            +GPIORead(...) *
            +GPIODirWrite(...) *
            +GPIOWrite(...) *

            +CustomParameterWrite(...) *
            +CustomParameterRead(...) *

            +ProgramWrite(...) *

            +ResetDevice(...) *
        }

        class ISerialPort {
            <<interface>>
            +Write(...) int *
            +Read(...) int *
        }

        class IDMA {
            <<interface>>
            +Enable(bool) *
            +EnableContinuous(...) *
            +GetCounters() State *
            +SubmitRequest(...) *
            +Wait() *
            +BufferOwnership(...) *
            +GetBuffers() Buffer[] *
        }       
    %%}

    %%namespace PCIeCommunications {
        class LimePCIeDMA {
            +Enable(bool)
            +EnableContinuous(...)
            +GetCounters() State
            +SubmitRequest(...)
            +Wait()
            +BufferOwnership(...)
            +GetBuffers() Buffer[]
        }

        class LimePCIe {
            +Device communication functions()
        }

        class LMS64C_LMS7002M_Over_PCIe {
            +SPI(...)
            +ResetDevice(...)

            -LimePCIe port
        }

        class LMS64C_FPGA_Over_PCIe {
            +SPI(...)
            +CustomParameterWrite(...)
            +CustomParameterRead(...)
            +ProgramWrite(...)

            -LimePCIe port
        }

        class LMS64C_LMS7002M_Over_PCIe_MMX8 {
            +SPI(...)
            +ResetDevice(...)

            -LimePCIe port
        }

        class LMS64C_FPGA_Over_PCIe_MMX8 {
            +SPI(...)
            +CustomParameterWrite(...)
            +CustomParameterRead(...)
            +ProgramWrite(...)

            -LimePCIe port
        }

        class LMS64C_ADF_Over_PCIe_MMX8 {
            +SPI(...)
            -LimePCIe port
        }

        class PCIE_CSR_Pipe {
            +Write(...) int
            +Read(...) int

            #LimePCIe port
        }
    %%}

    %%namespace Components {
        class ADF4002 {
            -ISPI controlPort
            +Device control functions()
        }

        class CDCM_Dev {
            -ISPI controlPort
            +Device control functions()
        }

        class Equalizer {
            -ISPI controlPort
            +Device control functions()
        }

        class FPGA {
            -ISPI fpgaPort
            -ISPI lms7002mPort

            +Device control functions()
        }

        class FPGA_Mini {
            +Device control functions()
        }

        class FPGA_X3 {
            +Device control functions()
        }          

        class FPGA_XTRX {
            +Device control functions()
        }
    %%}

    class LMS7002M_SDRDevice {
        <<abstract>>
        +GetDescriptor(...) SDRDescriptor

        +Reset(...)
        +GetGPSLock(...)

        +EnableChannel(...)

        +GetFrequency(...) double
        +SetFrequency(...)

        +GetNCOFrequency(...) double
        +SetNCOFrequency(...)

        +GetNCOIndex(...) int
        +SetNCOIndex(...)

        +GetNCOOffset(...) double

        +GetSampleRate(...)double

        +GetGain(...)
        +SetGain(...)

        +GetLowPassFilter(...) double
        +SetLowPassFilter(...)

        +GetAntenna(...) uint8_t
        +SetAntenna(...)

        +GetTestSignal(...) TestSignal
        +SetTestSignal(...)

        +GetDCOffsetMode(...) bool
        +SetDCOffsetMode(...)

        +GetDCOffset(...) complex64f_t
        +SetDCOffset(...)

        +GetIQBalance(...) complex64f_t
        +SetIQBalance(...)

        +GetCGENLocked(...) bool
        +GetTemperature(...) double
        +GetSXLocked(...) bool

        +ReadRegister(...) uint
        +WriteRegister(...)

        +LoadConfig(...)
        +SaveConfig(...)
        
        +GetParameter(...) uint16_t
        +SetParameter(...)

        +Calibrate(...)

        +ConfigureGFIR(...)
        +GetGFIRCoefficients(...) double[]
        +SetGFIRCoefficients(...)
        +SetGFIR(...)

        +Synchronize(...)
        +EnableCache(...)

        +GetHardwareTimestamp(...) uint64_t
        +SetHardwareTimestamp(...)

        +StreamStart(...)
        +StreamStop(...)

        +StreamRx(...)int
        +StreamTx(...)int
        +StreamStatus(...)

        +SPI(...)

        +SetMessageLogCallback(...)

        +GetInternalChip(...)

        +UploadMemory(...)

        #UpdateFPGAInterfaceFrequency(...) $
        #SetGainInformationInDescriptor(...)
        #LMS7002M mLMSChips[]
        #TRXLooper mStreamers[]
        #FPGA mFPGA
    }

    class SlaveSelectShim {
        -IComms port
        +SPI(...)
        +ResetDevice(...)
    }

    class TRXLooper {
        +GetHardwareTimestamp() uint64_t
        +SetHardwareTimestamp(uint64_t now)
        +Setup(...)
        +Start()
        +Stop()

        +StreamRx(...)int
        +StreamTx(...)int

        #RxSetup()int
        #ReceivePacketsLoop()
        #RxTeardown()

        #TxSetup()int
        #TransmitPacketsLoop()
        #TxTeardown()

        #FPGA fpga
        #LMS7002M lms
    }

    %% Device entry tree
    DeviceRegistryEntry <|-- USBEntry
    DeviceFactoryFX3 --|> USBEntry
    DeviceFactoryFTDI --|> USBEntry

    DeviceRegistryEntry <|-- DeviceFactoryPCIe

    SDRDevice <.. DeviceRegistry
    DeviceRegistry ..> DeviceHandle 
    DeviceHandle <.. DeviceRegistryEntry
    DeviceRegistry ..> DeviceRegistryEntry

    %% Interface implementations
    IComms ..|> ISPI

    USB_CSR_Pipe ..|> ISerialPort
    LMS64C_LMS7002M_Over_USB ..|> IComms
    LMS64C_FPGA_Over_USB ..|> IComms

    SlaveSelectShim ..|> ISPI
    PCIE_CSR_Pipe ..|> ISerialPort

    LMS64C_FPGA_Over_PCIe ..|> IComms
    LMS64C_LMS7002M_Over_PCIe ..|> IComms

    LMS64C_ADF_Over_PCIe_MMX8 ..|> ISPI
    LMS64C_FPGA_Over_PCIe_MMX8 ..|> IComms
    LMS64C_LMS7002M_Over_PCIe_MMX8 ..|> IComms

    USBDMAEmulation ..|> IDMA
    LimePCIeDMA ..|> IDMA

    %% SDRDevice implementations
    SDRDevice <|-- LMS7002M_SDRDevice
    SDRDevice <|-- LimeSDR_MMX8
    LMS7002M_SDRDevice <|-- LimeSDR
    LMS7002M_SDRDevice <|-- LimeSDR_Mini
    LMS7002M_SDRDevice <|-- LimeSDR_X3
    LMS7002M_SDRDevice <|-- LimeSDR_XTRX
    LimeSDR_XTRX <|-- XSDR

    %% USB entry connections
    DeviceFactoryFX3 ..> FX3
    DeviceFactoryFX3 ..> USB_CSR_Pipe_SDR
    DeviceFactoryFX3 ..> LMS64C_LMS7002M_Over_USB
    DeviceFactoryFX3 ..> LMS64C_FPGA_Over_USB

    DeviceFactoryFTDI ..> FT601
    DeviceFactoryFTDI ..> USB_CSR_Pipe_Mini
    DeviceFactoryFTDI ..> LMS64C_LMS7002M_Over_USB
    DeviceFactoryFTDI ..> LMS64C_FPGA_Over_USB

    %% USB connections
    LMS64C_LMS7002M_Over_USB --* USB_CSR_Pipe
    LMS64C_FPGA_Over_USB --* USB_CSR_Pipe

    USB_CSR_Pipe_Mini --* FT601
    USB_CSR_Pipe_SDR --* FX3

    USB_CSR_Pipe_Mini --|> USB_CSR_Pipe
    USB_CSR_Pipe_SDR --|> USB_CSR_Pipe

    FT601 --|> IUSB
    FX3 --|> IUSB

    USBGeneric --|> IUSB

    %% USB devices
    LimeSDR ..> IUSB
    LimeSDR ..> ISerialPort
    LimeSDR ..> IComms

    LimeSDR_Mini ..> IUSB
    LimeSDR_Mini ..> ISerialPort
    LimeSDR_Mini ..> IComms
    LimeSDR_Mini ..> FPGA_Mini

    USBDMAEmulation ..> IUSB

    %% PCIe connections
    PCIE_CSR_Pipe ..> LimePCIe
    SlaveSelectShim ..> IComms

    LMS64C_FPGA_Over_PCIe ..> PCIE_CSR_Pipe
    LMS64C_LMS7002M_Over_PCIe ..> PCIE_CSR_Pipe

    LMS64C_ADF_Over_PCIe_MMX8 ..> PCIE_CSR_Pipe
    LMS64C_FPGA_Over_PCIe_MMX8 ..> PCIE_CSR_Pipe
    LMS64C_LMS7002M_Over_PCIe_MMX8 ..> PCIE_CSR_Pipe

    LimePCIeDMA ..> LimePCIe

    %% PCIe entry connections
    DeviceFactoryPCIe ..> LimePCIe
    DeviceFactoryPCIe ..> LMS64C_LMS7002M_Over_PCIe
    DeviceFactoryPCIe ..> LMS64C_FPGA_Over_PCIe
    DeviceFactoryPCIe ..> LMS64C_LMS7002M_Over_PCIe_MMX8
    DeviceFactoryPCIe ..> LMS64C_FPGA_Over_PCIe_MMX8
    DeviceFactoryPCIe ..> LMS64C_ADF_Over_PCIe_MMX8

    %% PCIe devices
    LimeSDR_X3 ..> CDCM_Dev
    LimeSDR_X3 ..> Equalizer
    LimeSDR_X3 ..> LimePCIe
    LimeSDR_X3 ..> SlaveSelectShim
    LimeSDR_X3 ..> IComms
    FPGA_X3 <.. LimeSDR_X3 

    LimeSDR_XTRX ..> IComms
    LimeSDR_XTRX ..> LimePCIe
    LimeSDR_XTRX ..> FPGA_XTRX

    LimeSDR_MMX8 ..> IComms
    LimeSDR_MMX8 ..> LimePCIe
    LimeSDR_MMX8 ..> LimeSDR_XTRX
    LimeSDR_MMX8 ..> ADF4002

    %% Component dependencies
    LMS7002M ..> ISPI

    LMS7002M_SDRDevice ..> LMS7002M
    LMS7002M_SDRDevice ..> TRXLooper
    LMS7002M_SDRDevice ..> FPGA

    FPGA_Mini --|> FPGA
    FPGA <|-- FPGA_X3
    FPGA_XTRX --|> FPGA

    TRXLooper ..> FPGA
    TRXLooper ..> LMS7002M
    TRXLooper ..> IDMA

    ADF4002 ..> ISPI
    CDCM_Dev ..> ISPI
    Equalizer ..> ISPI
    FPGA ..> ISPI
